# -*- coding: UTF-8 -*-
from django.core import template_loader
from django.core.exceptions import Http404, ObjectDoesNotExist
from django.core.extensions import DjangoContext as Context
from django.core.xheaders import populate_xheaders
from django.models import get_module
from django.utils.httpwrappers import HttpResponse
import datetime, time

def archive_index(request, app_label, module_name, date_field, num_latest=15, template_name=None, extra_lookup_kwargs={}, extra_context=None):
    """
    Archive de haut niveau générique pour les objets datés.

    Modèles : ``<app_label>/<module_name>_archive``
    Contexte :
        date_list
            La liste des années
        latest
            Les N (par défaut, 15) derniers objets triés par date
    """
    mod = get_module(app_label, module_name)
    lookup_kwargs = {'%s__lte' % date_field: datetime.datetime.now()}
    lookup_kwargs.update(extra_lookup_kwargs)
    date_list = getattr(mod, "get_%s_list" % date_field)('year', **lookup_kwargs)[::-1]
    if not date_list:
        raise Http404("Aucun objet %s.%s n'est disponible" % (app_label, module_name))

    if num_latest:
        lookup_kwargs.update({
            'limit': num_latest,
            'order_by': ((date_field, 'DESC'),),
        })
        latest = mod.get_list(**lookup_kwargs)
    else:
        latest = None

    if not template_name:
        template_name = "%s/%s_archive" % (app_label, module_name)
    t = template_loader.get_template(template_name)
    c = Context(request, {
        'date_list' : date_list,
        'latest' : latest,
    })
    if extra_context:
        c.update(extra_context)
    return HttpResponse(t.render(c))

def archive_year(request, year, app_label, module_name, date_field, template_name=None, extra_lookup_kwargs={}, extra_context=None):
    """
    Vue générique implémentant une archive annuelle.

    Modèles : ``<app_label>/<module_name>_archive_year``
    Contexte :
        date_list
            La liste des mois de l'année de l'archive pour lesquels des objets existent
        year
            L'année de l'archive
    """
    mod = get_module(app_label, module_name)
    now = datetime.datetime.now()
    lookup_kwargs = {'%s__year' % date_field: year}
    # On ne compare avec la date actuelle que si l'année de l'archive n'est pas une année passée.
    if int(year) >= now.year:
        lookup_kwargs['%s__lte' % date_field] = now
    lookup_kwargs.update(extra_lookup_kwargs)
    date_list = getattr(mod, "get_%s_list" % date_field)('month', **lookup_kwargs)
    if not date_list:
        raise Http404
    if not template_name:
        template_name = "%s/%s_archive_year" % (app_label, module_name)
    t = template_loader.get_template(template_name)
    c = Context(request, {
        'date_list': date_list,
        'year': year,
    })
    if extra_context:
        c.update(extra_context)
    return HttpResponse(t.render(c))

def archive_month(request, year, month, app_label, module_name, date_field, template_name=None, extra_lookup_kwargs={}, extra_context=None):
    """
    Vue générique implémentant une archive mensuelle.

    Modèles : ``<app_label>/<module_name>_archive_month``
    Contexte :
        month:
            le mois de l'archive
        object_list:
            la liste des objets qui ont été publiés lors du mois spécifié
    """
    try:
        date = datetime.date(*time.strptime(year+month, '%Y%b')[:3])
    except ValueError:
        raise Http404
    mod = get_module(app_label, module_name)
    now = datetime.datetime.now()
    # On détermine le premier et le dernier jour du mois, afin d'utiliser une recherche type "intervalle de temps".
    first_day = date.replace(day=1)
    last_day = date
    for i in (31, 30, 29, 28):
        try:
            last_day = last_day.replace(day=i)
        except ValueError:
            continue
        else:
            break
    lookup_kwargs = {'%s__range' % date_field: (first_day, last_day)}
    # On ne compare avec la date actuelle que si le mois de l'archive n'est pas un mois passé.
    if date >= now:
        lookup_kwargs['%s__lte' % date_field] = now
    lookup_kwargs.update(extra_lookup_kwargs)
    object_list = mod.get_list(**lookup_kwargs)
    if not object_list:
        raise Http404
    if not template_name:
        template_name = "%s/%s_archive_month" % (app_label, module_name)
    t = template_loader.get_template(template_name)
    c = Context(request, {
        'object_list': object_list,
        'month': date,
    })
    if extra_context:
        c.update(extra_context)
    return HttpResponse(t.render(c))

def archive_day(request, year, month, day, app_label, module_name, date_field, template_name=None, extra_lookup_kwargs={}, extra_context=None, allow_empty=False):
    """
    Vue générique implémentant une archive quotidienne.

    Modèles : ``<app_label>/<module_name>_archive_day``
    Contexte :
        object_list:
            la liste des objets publiés lors du jour de l'archive
        day:
            (datetime) le jour de l'archive
        previous_day
            (datetime) le jour précédent
        next_day
            (datetime) le jour suivant, ou None si le jour de l'archive est aussi le jour actuel
    """
    try:
        date = datetime.date(*time.strptime(year+month+day, '%Y%b%d')[:3])
    except ValueError:
        raise Http404
    mod = get_module(app_label, module_name)
    now = datetime.datetime.now()
    lookup_kwargs = {
        '%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
    }
    # On ne compare avec la date actuelle que si la date de l'archive n'est pas une date passée.
    if date >= now:
        lookup_kwargs['%s__lte' % date_field] = now
    lookup_kwargs.update(extra_lookup_kwargs)
    object_list = mod.get_list(**lookup_kwargs)
    if not allow_empty and not object_list:
        raise Http404
    if not template_name:
        template_name = "%s/%s_archive_day" % (app_label, module_name)
    t = template_loader.get_template(template_name)
    c = Context(request, {
        'object_list': object_list,
        'day': date,
        'previous_day': date - datetime.timedelta(days=1),
        'next_day': (date < datetime.date.today()) and (date + datetime.timedelta(days=1)) or None,
    })
    if extra_context:
        c.update(extra_context)
    return HttpResponse(t.render(c))

def archive_today(request, **kwargs):
    """
    Vue générique implémentant une archive quotidienne pour le jour actuel. Même chose que la vue archive_day.
    """
    today = datetime.date.today()
    kwargs.update({
        'year': str(today.year),
        'month': today.strftime('%b').lower(),
        'day': str(today.day),
    })
    return archive_day(request, **kwargs)

def object_detail(request, year, month, day, app_label, module_name, date_field, object_id=None, slug=None, slug_field=None, template_name=None, extra_lookup_kwargs={}, extra_context=None):
    """
    Vue générique permettant d'obtenir les détails d'un objet à partir d'une URL style année/mois/jour/slug ou année/mois/jour/id.

    Modèles : ``<app_label>/<module_name>_detail``
    Contexte :
        object:
            l'objet devant être détaillé
    """
    try:
        date = datetime.date(*time.strptime(year+month+day, '%Y%b%d')[:3])
    except ValueError:
        raise Http404
    mod = get_module(app_label, module_name)
    now = datetime.datetime.now()
    lookup_kwargs = {
        '%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
    }
    # On ne compare avec la date actuelle que si la date n'est pas une date passée.
    if date >= now:
        lookup_kwargs['%s__lte' % date_field] = now
    if object_id:
        lookup_kwargs['%s__exact' % mod.Klass._meta.pk.name] = object_id
    elif slug and slug_field:
        lookup_kwargs['%s__exact' % slug_field] = slug
    else:
        raise AttributeError("Une vue générique permettant d'obtenir les détails d'un objet doit être appelée en spécifiant l'identifiant de l'objet ou un slug (champ SlugField)")
    lookup_kwargs.update(extra_lookup_kwargs)
    try:
        object = mod.get_object(**lookup_kwargs)
    except ObjectDoesNotExist:
        raise Http404("Aucun objet %s.%s ne correspond aux critères de recherche %s" % (app_label, module_name, lookup_kwargs))
    if not template_name:
        template_name = "%s/%s_detail" % (app_label, module_name)
    t = template_loader.get_template(template_name)
    c = Context(request, {
        'object': object,
    })
    if extra_context:
        c.update(extra_context)
    response = HttpResponse(t.render(c))
    populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
    return response
